package tasks;

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.ArrayList;
import java.util.StringTokenizer;
import java.util.Vector;

import javax.swing.AbstractButton;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.KeyStroke;

import api.Client2Master;

/**
 * The class which presents a GUI to the user allowing it 
 * to launch different map/reduce tasks (inverted index, 
 * word count). Once these tasks are done, it presents the 
 * user with a lookup interface that allows
 * the user to search the index created prior to the lookup.
 * In addition it also allows the user to check on the status
 * of the Map/Reduce System
 * 
 * This client is implemented using Swing and communicates with
 * the Master using Client2Master interface
 * 
 * @author Mayuri Karra, Bing Wei
 *
 */
public class MapReduceClient  extends JPanel
implements ActionListener {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	final static int N_PIXELS = 256;
	static Client2Master master;
	static Registry registry;
	
	JButton lookUpButtonInvIndex;
	JButton lookUpButtonWordCount;
	
	JTextArea statusTextArea;
	JTextField lookUpField;
	JScrollPane areaScrollPane;
	JMenuBar menuBar;
    JMenu statusMenu, appMenu;
    JMenuItem getStatusItem, invertedIndexItem, wordCountItem;
    JPanel contentPane;
    long taskId = -1;

    static final String dataSourceDirPrefix = "/cs/student/bwei/data/";
    //static final String dataSourceDirPrefix = "/tmp/data/";
    static final String invertedIndexInput = dataSourceDirPrefix + "invertedindex/in.txt";	
    static final String wordCountInput =  dataSourceDirPrefix + "wordcount/in.txt";	
    static final String httpInvertedIndexPrefix = dataSourceDirPrefix + "invertedindex/";
    static final String httpWordCountPrefix = dataSourceDirPrefix + "wordcount/";

    String inputFile;
    enum taskType{
	  invertedIndexTask,
	  wordCountTask
    };
    taskType t;
   
    /**
     * The constructor. It essentially sets the size of the GUI window
     */
	public MapReduceClient(){
		
		try {        
			setPreferredSize(new Dimension(400, 500));
			
		} catch (Exception e) {
			e.printStackTrace();
		} 
	}

	

	/**
	 * method to create the menu bar in GUI
	 * @return a menu bar
	 */
    public JMenuBar createMenuBar() {
    
        menuBar = new JMenuBar();

        //Build the first menu.
        statusMenu = new JMenu("Status");
        statusMenu.setMnemonic(KeyEvent.VK_A);
        statusMenu.getAccessibleContext().setAccessibleDescription(
                "The only menu in this program that has menu items");
        
        getStatusItem = new JMenuItem("Get Status",
                KeyEvent.VK_T);
        getStatusItem.setAccelerator(KeyStroke.getKeyStroke(
                KeyEvent.VK_1, ActionEvent.ALT_MASK));
		getStatusItem.getAccessibleContext().setAccessibleDescription(
		   "This doesn't really do anything");
		getStatusItem.addActionListener(this);
		getStatusItem.setActionCommand("getStatus");
		statusMenu.add(getStatusItem);

        
        menuBar.add(statusMenu);
        
        //Build second menu in the menu bar.
        appMenu = new JMenu("Applications");
        appMenu.setMnemonic(KeyEvent.VK_N);
        appMenu.getAccessibleContext().setAccessibleDescription(
                "This menu does nothing");
        menuBar.add(appMenu);
        
        //a group of JMenuItems
        invertedIndexItem = new JMenuItem("Inverted Index",
                                 KeyEvent.VK_T);
        //menuItem.setMnemonic(KeyEvent.VK_T); //used constructor instead
        invertedIndexItem.setAccelerator(KeyStroke.getKeyStroke(
                KeyEvent.VK_2, ActionEvent.ALT_MASK));
        invertedIndexItem.getAccessibleContext().setAccessibleDescription(
                "This doesn't really do anything");
        invertedIndexItem.addActionListener(this);
        invertedIndexItem.setActionCommand("launchInvIndex");
        appMenu.add(invertedIndexItem);
        
        wordCountItem = new JMenuItem("Word Count Task",
                KeyEvent.VK_T);
		//menuItem.setMnemonic(KeyEvent.VK_T); //used constructor instead
		wordCountItem.setAccelerator(KeyStroke.getKeyStroke(
				KeyEvent.VK_3, ActionEvent.ALT_MASK));
		wordCountItem.getAccessibleContext().setAccessibleDescription(
			"This doesn't really do anything");
		wordCountItem.addActionListener(this);
		wordCountItem.setActionCommand("launchWordCount");
		appMenu.add(wordCountItem);

        
        add(menuBar, BorderLayout.WEST);

        return menuBar;    	
    }
    
    public Container createContentPane(){
    	//Create the content-pane-to-be.
        contentPane = new JPanel(new BorderLayout());
        contentPane.setOpaque(true);

        //Create a scrolled text area.
        statusTextArea = new JTextArea(5, 5);
        statusTextArea.setEditable(false);
        areaScrollPane = new JScrollPane(statusTextArea);

        //Add the text area to the content pane.
        contentPane.add(areaScrollPane, BorderLayout.CENTER);

        return contentPane;
    }
	
    /**
     * The helper function that retrieves the status of the
     * map/reduce system and sets it in the text area
     */
	public void setStatusPanel(){
		Vector<String> status = new Vector<String>();
		try {
			status = master.ping();
		} catch (RemoteException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		StringBuffer str = new StringBuffer();
		for(String oneStatus: status)
			str.append(oneStatus+"\n");
		
	    //Create a text area.
	    statusTextArea = new JTextArea(str.toString());
	    statusTextArea.setFont(new Font("Serif", Font.ITALIC, 16));
	    statusTextArea.setLineWrap(true);
	    statusTextArea.setWrapStyleWord(true);
	   
	    if(areaScrollPane != null)
	    	contentPane.remove(areaScrollPane);

	    if (lookUpField != null)
	    	contentPane.remove(lookUpField);

	    if (lookUpButtonInvIndex != null)
	    	contentPane.remove(lookUpButtonInvIndex);

	    if (lookUpButtonWordCount != null)
	    	contentPane.remove(lookUpButtonWordCount);

	    contentPane.revalidate();
	    contentPane.repaint();
	
	    areaScrollPane = new JScrollPane(statusTextArea);
	    areaScrollPane.setVerticalScrollBarPolicy(
	                    JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
	    areaScrollPane.setPreferredSize(new Dimension(250, 250));
	    areaScrollPane.setBorder(
	        BorderFactory.createCompoundBorder(
	            BorderFactory.createCompoundBorder(
	                            BorderFactory.createTitledBorder(""),
	                            BorderFactory.createEmptyBorder(5,5,5,5)),
	            areaScrollPane.getBorder()));
	    contentPane.add(areaScrollPane);
	    contentPane.revalidate();
	    contentPane.repaint();
	    
	}


	/**
	 * The function to launch the inverted index task on Master
	 */
	public void launchInvertedIndex(){
		try {
			long startTime = System.currentTimeMillis();
			InvertedIndexTask task = new InvertedIndexTask(invertedIndexInput, "invertedindex");
			master.addTask(task);
			long workerTime = master.finish();
			long endTime = System.currentTimeMillis();
			System.out.println("Time seen by the client: " + (endTime-startTime));
			System.out.println("Time seen by workers: " + workerTime);
			System.out.println("DONE with generating the inverted index !!!");
			taskId++;

		} catch (RemoteException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		

	    if(areaScrollPane != null)
	    	contentPane.remove(areaScrollPane);

	    if (lookUpField != null)
	    	contentPane.remove(lookUpField);

	    if (lookUpButtonInvIndex != null)
	    	contentPane.remove(lookUpButtonInvIndex);

	    if (lookUpButtonWordCount != null)
	    	contentPane.remove(lookUpButtonWordCount);


	    contentPane.revalidate();
	    contentPane.repaint();

	    //Create a text area.
	    statusTextArea = new JTextArea("");
	    statusTextArea.setFont(new Font("Serif", Font.ITALIC, 16));
	    statusTextArea.setLineWrap(true);
	    statusTextArea.setWrapStyleWord(true);
	   
		lookUpButtonInvIndex = new JButton("LookUp");
        lookUpButtonInvIndex.setVerticalTextPosition(AbstractButton.CENTER);
        lookUpButtonInvIndex.setMnemonic(KeyEvent.VK_D);
        lookUpButtonInvIndex.setActionCommand("doLookUpInvertedIndex");	       
        //Listen for actions on buttons 1 and 3.
        lookUpButtonInvIndex.addActionListener(this);


	    lookUpField = new JTextField("");
	    lookUpField.setColumns(30);

	    areaScrollPane = new JScrollPane(statusTextArea);
	    areaScrollPane.setVerticalScrollBarPolicy(
	                    JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
	    areaScrollPane.setPreferredSize(new Dimension(250, 150));
	    areaScrollPane.setBorder(
	        BorderFactory.createCompoundBorder(
	            BorderFactory.createCompoundBorder(
	                            BorderFactory.createTitledBorder(""),
	                            BorderFactory.createEmptyBorder(5,5,5,5)),
	            areaScrollPane.getBorder()));
	    
	    contentPane.add(lookUpField, BorderLayout.WEST);
	    contentPane.add(lookUpButtonInvIndex, BorderLayout.EAST);
	    contentPane.add(areaScrollPane, BorderLayout.SOUTH);

	    contentPane.revalidate();
	    contentPane.repaint();
	    
	}

	/**
	 * The function to lookup the inverted index task created on Master
	 */

	public void doLookUpInvertedIndex(String key){
		String result = null;
		StringBuffer resultArr = new StringBuffer();
		try {
			 result = master.lookup(taskId, key);
			 result = result.substring(1, result.length()-1);
			 System.out.println("----------------------");
			 System.out.println("Result is " + result);
			 System.out.println("----------------------");
			 StringTokenizer tokenize = new StringTokenizer(result, ",");
			 
			 BufferedReader input = null;
				
			input =  new BufferedReader(new FileReader(inputFile));
			ArrayList<String> lines = new ArrayList<String>();
	        String line = null; //not declared within while loop
	        
	        //TODO change the read
        	while (( line = input.readLine()) != null){
	        	lines.add(line);
        	}
            input.close();
        	 
			 while(tokenize.hasMoreTokens()){
				 String onePair = tokenize.nextToken().trim();
				 String[] strs = onePair.split(":");
				 String fileName = lines.get(Integer.valueOf(strs[0]));
				 String httpLink = fileName.substring(httpInvertedIndexPrefix.length(), fileName.length() - ".txt".length());
				 resultArr.append(strs[1] + " : " +  httpLink +  "\n");
			 }
			 
			 System.out.println("DONE with looking up the inverted index " + result);
		} catch (RemoteException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException ex){
		      ex.printStackTrace();
	    }

	    //Create a text area.
	    statusTextArea.setText(resultArr.toString());
	    contentPane.revalidate();
	    contentPane.repaint();
	
	    contentPane.repaint();
	    
	}

	/**
	 * The function to launch the word count task on Master
	 */

	public void launchWordCount(){
		try {
			long startTime = System.currentTimeMillis();
			WordCountTask task = new WordCountTask(wordCountInput, "wordcount");
			master.addTask(task);
			long workerTime = master.finish();
			long endTime = System.currentTimeMillis();
			System.out.println("Time seen by the client: " + (endTime-startTime));
			System.out.println("Time seen by workers: " + workerTime);
			System.out.println("DONE with generating the word count index !!!");
			taskId++;
		} catch (RemoteException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		

	    if(areaScrollPane != null)
	    	contentPane.remove(areaScrollPane);

	    if (lookUpField != null)
	    	contentPane.remove(lookUpField);

	    if (lookUpButtonInvIndex != null)
	    	contentPane.remove(lookUpButtonInvIndex);

	    if (lookUpButtonWordCount != null)
	    	contentPane.remove(lookUpButtonWordCount);

	    lookUpField = new JTextField("");
	    lookUpField.setColumns(30);

	    //Create a text area.
	    statusTextArea = new JTextArea("");
	    statusTextArea.setFont(new Font("Serif", Font.ITALIC, 16));
	    statusTextArea.setLineWrap(true);
	    statusTextArea.setWrapStyleWord(true);
	   
		lookUpButtonWordCount = new JButton("LookUp");
        lookUpButtonWordCount.setVerticalTextPosition(AbstractButton.CENTER);
        lookUpButtonWordCount.setMnemonic(KeyEvent.VK_D);
        lookUpButtonWordCount.setActionCommand("doLookUpWordCount");	       
        //Listen for actions on buttons 1 and 3.
        lookUpButtonWordCount.addActionListener(this);

	    
	    contentPane.revalidate();
	    contentPane.repaint();
	
	    areaScrollPane = new JScrollPane(statusTextArea);
	    areaScrollPane.setVerticalScrollBarPolicy(
	                    JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
	    areaScrollPane.setPreferredSize(new Dimension(250, 150));
	    areaScrollPane.setBorder(
	        BorderFactory.createCompoundBorder(
	            BorderFactory.createCompoundBorder(
	                            BorderFactory.createTitledBorder(""),
	                            BorderFactory.createEmptyBorder(5,5,5,5)),
	            areaScrollPane.getBorder()));
	    
	    contentPane.add(lookUpField, BorderLayout.WEST);
	    contentPane.add(lookUpButtonWordCount, BorderLayout.EAST);
	    contentPane.add(areaScrollPane, BorderLayout.SOUTH);

	    contentPane.revalidate();
	    contentPane.repaint();
	    
	}

	/**
	 * The function to lookup the word count index created on Master
	 */

	public void doLookUpWordCount(String key){
		String result = null;
		try {
			 result = master.lookup(taskId, key);			 
			 System.out.println("DONE with looking up the word count index for " + key + " result "  + result);
		} catch (RemoteException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	    //Create a text area.
	    statusTextArea.setText(result);
	    contentPane.revalidate();
	    contentPane.repaint();
	
	    contentPane.repaint();
	    
	}


	@Override
	public void actionPerformed(ActionEvent e) {
		// TODO Auto-generated method stub
        System.out.println("The action command is " + e.getActionCommand());
        if ("getStatus".equals(e.getActionCommand())) {
            setStatusPanel();
        }	

        if ("launchInvIndex".equals(e.getActionCommand())) {
        	inputFile = invertedIndexInput;
            launchInvertedIndex();
        }	

        if ("launchWordCount".equals(e.getActionCommand())) {
        	inputFile = wordCountInput;
        	launchWordCount();
        }	

        if ("doLookUpInvertedIndex".equals(e.getActionCommand())) {
            doLookUpInvertedIndex(lookUpField.getText());
        }	

        if ("doLookUpWordCount".equals(e.getActionCommand())) {
            doLookUpWordCount(lookUpField.getText());
        }	

	}


    public static void main(String[] args) {
        //Schedule a job for the event-dispatching thread:
        //creating and showing this application's GUI.
    	String masterName = args[0];
 	
    	try{
			registry = LocateRegistry.getRegistry(masterName);
			master = (Client2Master)registry.lookup(Client2Master.SERVICE_NAME);
    	}catch(Exception e){
    		e.printStackTrace();
    	}
    	
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI(); 
            }
        });
    }

    /**
     * the function that creates GUI
     */
    public static void createAndShowGUI(){
 
        
		JFrame frame = new JFrame("MapReduce by AMAzing Team");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	
		
		MapReduceClient client = new MapReduceClient();
		frame.setJMenuBar(client.createMenuBar());
		frame.setContentPane(client.createContentPane());


        //Display the window.
        frame.setSize(450, 260);
        frame.setVisible(true);
        
        System.out.println("createAndShowGUI");
		
    }
	
}
